<?php

/*
 * Session类
 * @author sufei <87753586@qq.com>
 * @date 2012-07-03
 */

class Session {

	//生命时间，单位秒
	private $lifetime = 1800;
	
	//session键名前缀
	private $prefix = 'session_';
	
	//保存方式，database.数据库，memcached.memcached，为空使用默认$_SESSION
	private $save_mode = 'memcached';
	
	//是否要匹配ip
	private $match_ip = TRUE;
	
	//是否要匹配
	private $match_user_agent = TRUE;
	
	//数据库链接
	private $db;
	
	//数据库地址
	private $db_host = 'localhost';
	
	//数据库端口
	private $db_port = 3306;
	
	//数据库用户
	private $db_user = 'root';
	
	//数据库密码
	private $db_pass = '';
	
	//数据库名称
	private $db_name = 'fg';
	
	//数据库session表名称
	private $db_table = 'session';
	
	//memcached链接
	private $memcached;
	
	//memcached地址
	private $memcached_host = '127.0.0.1';
	
	//memcached端口号
	private $memcached_port = 11211;
	
	//memcached连接超时时间
	private $memcached_timeout = 1;
	
	//错误信息
	private $error = '';

	public function __construct($config = array())
	{
		$defaults = array(
				'lifetime',
				'prefix',
				'save_mode',
				'match_ip',
				'match_user_agent',
				'db_host',
				'db_port',
				'db_user',
				'db_pass',
				'db_name',
				'db_table',
				'memcached_host',
				'memcached_port',
				'memcached_timeout'
		);
		
		if ($config && is_array($config))
		{	
			foreach ($defaults as $key)
			{
				if (isset($config[$key]))
				{
					$method = 'set_'.$key;
					if (method_exists($this, $method))
					{
						$this->$method($config[$key]);
					}
					else
					{
						$this->$key = $config[$key];
					}
				}
			}
		}
		
		$this->init();
	}
	
	/*
	 * 初始化
	 * @access protected
	 * @return void
	 */
	protected function init()
	{
		if ($this->save_mode != FALSE && $this->save_mode != 'default')
		{
			if ( ! function_exists('session_set_save_handler'))
			{
				exit('服务器不支持session_set_save_handler函数');
			}
			session_set_save_handler(array(&$this, '_open'), array(&$this, '_close'), array(&$this, '_read'), array(&$this, '_write'), array(&$this, '_destroy'), array(&$this, '_gc'));
		}
		
		//开启session
		if ( ! session_id())
		{
			session_start();
		}
		
		//首先检查生命周期
		if ($this->is_expired())
		{
			$this->destroy();
		}
		
		//如果开启ip匹配，这里判断是否匹配ip
		if ($this->match_ip && ! $this->is_match_ip())
		{
			$this->destroy();
		}
		
		//如果开启用户代理匹配，这里判断是否匹配user_agent
		if ($this->match_user_agent && ! $this->is_match_user_agent())
		{
			$this->destroy();
		}
		
		//销毁已经闪出的数据
		$this->flash_sweep();
		
		//标记闪出数据已经闪出
		$this->flash_mark();
		
		//修改最近访问时间
		$this->set('last_access', time());
	}
	
	/*
	 * 写入session
	 * @access public
	 * @param $name string session名称
	 * @param $value mixed session值
	 * @param $flash boolean 是否闪出
	 * @return boolean
	 */
	public function set($name, $value, $flash = FALSE)
	{
		//检查database、memcached链接
		if ( ! $this->validate_link())
		{
			return FALSE;
		}
		
		if ($name=='session_id')
		{
			session_id($value);
		}
		elseif ($name=='session_name')
		{
			session_name($value);
		}
		else
		{
			if (strpos($name, '_new_')!==0 && strpos($name, '_old_')!==0)
			{
				$name = $this->prefix.$name;
				$name = $flash ? ('_new_'.$name) : $name;
			}
			$_SESSION[$name] = $value;
		}
		
		return TRUE;
	}
	
	/*
	 * 返回session
	 * @access public
	 * @param $name
	 * $param $flash boolean 是否闪出
	 * @return mixed
	 */
	public function get($name, $flash = FALSE)
	{
		//检查database、memcached链接
		if ( ! $this->validate_link())
		{
			return FALSE;
		}

		if ($name=='session_id')
		{
			return session_id();
		}
		elseif ($name=='session_name')
		{
			return session_name();
		}
		elseif ($flash)
		{
			$name = '_old_'.$this->prefix.$name;
		}
		else{
			$name = $this->prefix.$name;
		}

		return isset($_SESSION[$name]) ? $_SESSION[$name] : FALSE;
	}

	/*
	 * 删除
	 * @access public
	 * @param $name string session名称
	 * @return boolean
	 */
	public function delete($name)
	{
		//检查database、memcached链接
		if ( ! $this->validate_link())
		{
			return FALSE;
		}
		
		if (strpos($name, '_new_')!==0 && strpos($name, '_old_')!==0)
		{
			$name = $this->prefix.$name;
		}
		
		if (isset($_SESSION[$name]))
		{
			unset($_SESSION[$name]);
		}
		
		return TRUE;
	}
	
	/*
	 * 销毁
	 * @access public
	 * @return boolean
	 */
	public function destroy()
	{
		//检查database、memcached链接
		if ( ! $this->validate_link())
		{
			return FALSE;
		}

		session_unset();
		//删除掉cookie值
		if (isset($_COOKIE[session_name()]))
		{
			unset($_COOKIE[session_name()]);
			setcookie(session_name(), '', time()-42000, '/');
		}
		session_destroy();
		
		return TRUE;
	}

	/*
	 * 设置闪出session
	 * @access public
	 * @param $name string session名称
	 * @param $value mixed session值
	 * @return boolean
	 */
	public function set_flash($name, $value)
	{
		$this->set($name, $value, TRUE);
	}
	
	/*
	 * 返回闪出session
	 * @access public
	 * @param $name string session名称
	 * @return mixed
	 */
	public function get_flash($name)
	{
		return $this->get($name, TRUE);
	}
	
	/*
	 * 将闪出session持久化
	 * @access public
	 * @return void
	 */
	public function keep_flash($name)
	{
		if (isset($_SESSION['_old_'.$this->prefix.$name]))
		{
			$this->set($name, $_SESSION['_old_'.$this->prefix.$name]);
			$this->delete('_old_'.$this->prefix.$name);
		}
	}

	/*
	 * 标记闪出数据
	 * @access protected
	 * @return void
	 */
	protected function flash_mark()
	{
		//把_new_标记的改为_old_标记，待下次清除
		foreach ($_SESSION as $name => $value)
		{
			$arr = explode('_new_', $name);
			if (is_array($arr) && count($arr)==2)
			{
				$new_name = '_old_'.$arr[1];
				$this->set($new_name, $value);
				$this->delete($name);
			}
		}
	}
	
	/*
	 * 销毁闪出数据
	 * @access protected
	 * @return void
	 */
	protected function flash_sweep()
	{
		//删除_old_标记的
		foreach ($_SESSION as $name => $value)
		{
			if (strpos($name, '_old_')===0)
			{
				$this->delete($name);
			}
		}
	}
	
	/*
	 * 判断是否过期
	 * @access protected
	 * @return boolean
	 */
	protected function is_expired()
	{
		if ( ! $this->get('last_access'))
		{
			$this->set('last_access', time());
			return FALSE;
		}
		
		return ($this->lifetime < (time()-$this->get('last_access'))) ? TRUE : FALSE;
	}
	
	/*
	 * 判断是否匹配ip
	 * @access protected
	 * @return boolean
	 */
	protected function is_match_ip()
	{
		if ( ! $this->get('ip'))
		{
			$this->set('ip', $this->get_ip());
			return TRUE;
		}
		
		return $this->get_ip()==$this->get('ip') ? TRUE : FALSE;
	}
	
	/*
	 * 判断是否匹配user_agent
	 * @access protected
	 * @return boolean
	 */
	protected function is_match_user_agent()
	{
		$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
		
		if ( ! $this->get('user_agent'))
		{
			$this->set('user_agent', $user_agent);
			return TRUE;
		}
		
		return $user_agent==$this->get('user_agent') ? TRUE : FALSE;
	}
	
	/*
	 * 返回客户端ip
	 * @access protected
	 * @return string
	 */
	protected function get_ip()
	{
		static $ip;
		
		if ($ip)
		{
			return $ip;
		}
		
		if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
		{
			$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
			$pos =  array_search('unknown', $arr);
			if (false !== $pos)
			{
				unset($arr[$pos]);
			}
			$ip   =  trim($arr[0]);
		}
		elseif (isset($_SERVER['HTTP_CLIENT_IP']))
		{
			$ip = $_SERVER['HTTP_CLIENT_IP'];
		}
		elseif (isset($_SERVER['REMOTE_ADDR']))
		{
			$ip = $_SERVER['REMOTE_ADDR'];
		}
		
		return $ip;
	}
	
	/*
	 * 检查database、memcached链接
	 * @access protected
	 * @return boolean
	 */
	protected function validate_link()
	{
		if ($this->save_mode != FALSE && $this->save_mode != 'default' && ! $this->db && ! $this->memcached)
		{
			return FALSE;
		}
		return TRUE;
	}
	
	/*
	 * 设置生命周期
	 * @access protected
	 * @return void
	 */
	protected function set_lifetime($lifetime)
	{
		$this->lifetime = intval($lifetime) ? intval($lifetime) : $this->lifetime;
	}
	
	/*
	 * 设置是否匹配ip
	 * @access protected
	 * @return void
	 */
	protected function set_match_ip($match_ip)
	{
		$this->match_ip = $match_ip ? TRUE : FALSE;
	}
	
	/*
	 * 设置是否匹配user_agent
	 * @access protected
	 * @return void
	 */
	protected function set_match_user_agent($match_user_agent)
	{
		$this->match_user_agent = $match_ip ? TRUE : FALSE;
	}
	
	/*
	 * 设置数据库端口号
	 * @access protected
	 * @return void
	 */
	protected function set_db_port($db_port)
	{
		$this->db_port = intval($db_port) ? intval($db_port) : $this->db_port;
	}
	
	/*
	 * 设置memcached端口号
	 * @access protected
	 * @return void
	 */
	protected function set_memcached_port($memcached_port)
	{
		$this->memcached_port = intval($memcached_port) ? intval($memcached_port) : $this->memcached_port;
	}
	
	/*
	 * 设置memcached连接超时时间
	 * @access protected
	 * @return void
	 */
	protected function set_memcached_timeout($memcached_timeout)
	{
		$this->memcached_timeout = intval($memcached_timeout) ? intval($memcached_timeout) : $this->memcached_timeout;
	}

	//================================================================================================================
	
	/*
	 * 打开链接
	 * @access public
	 * @return boolean
	 */
	public function _open()
	{
		$method = '_open_'.$this->save_mode;
		if (method_exists($this, $method))
		{
			return $this->$method();
		}
		return FALSE;
	}
	
	/*
	 * 打开数据库链接
	 * @access protected
	 * @return boolean
	 */
	protected function _open_database()
	{
		//链接数据库
		$this->db = @mysql_connect($this->db_host.$this->db_port, $this->db_user, $this->db_pass);
		//是否连上数据库表
		if ($this->db)
		{
			return mysql_select_db($this->db_name, $this->db);
		}
		else
		{
			exit('连接数据库失败');
		}
		
		return FALSE;
	}
	
	/*
	 * 打开memcached链接
	 * @access protected
	 * @return boolean
	 */
	protected function _open_memcached()
	{
		$this->memcached = new Memcache();
		if (@$this->memcached->connect($this->memcached_host, $this->memcached_port, $this->memcached_timeout))
		{
			return TRUE;
		}
		else
		{
			exit('连接Memcached失败');
			$this->memcached = FALSE;
			return FALSE;
		}
	}
	
	/*
	 * 关闭链接
	 * @access public
	 * @return boolean
	 */
	public function _close()
	{
		$method = '_close_'.$this->save_mode;
		if (method_exists($this, $method))
		{
			return $this->$method();
		}
		return FALSE;
	}
	
	/*
	 * 关闭数据库链接
	 * @access protected
	 * @return boolean
	 */
	protected function _close_database()
	{
		return $this->db ? mysql_close($this->db) : FALSE;
	}
	
	/*
	 * 关闭memcached链接
	 * @access protected
	 * @return boolean
	 */
	protected function _close_memcached()
	{
		if ( ! $this->memcached)
		{
			return FALSE;
		}
		return $this->memcached->close();
	}
	
	/*
	 * 读取
	 * @access public
	 * @param $id string 键值
	 * @return string
	 */
	public function _read($id)
	{
		$method = '_read_'.$this->save_mode;
		if (method_exists($this, $method))
		{
			return $this->$method($id);
		}
		return '';
	}
	
	/*
	 * 从数据库读取
	 * @access protected
	 * @param $id string 键值
	 * @return string
	 */
	protected function _read_database($id)
	{
		if ( ! $this->db)
		{
			return '';
		}
		//使用mysql的一个过滤函数过滤下
		$id = mysql_real_escape_string($id);
		$sql = "select session_data from ".$this->db_table." where session_id = '".$id."'";
		$result = mysql_query($sql, $this->db);
		if ($result && mysql_num_rows($result))
		{
			$record = mysql_fetch_assoc($result);
			return $record['session_data'];
		}
		return '';
	}
	
	/*
	 * 从memcached读取
	 * @access protected
	 * @param $id string 键值
	 * @return string
	 */
	protected function _read_memcached($id)
	{
		if ( ! $this->memcached)
		{
			return '';
		}
		$data = $this->memcached->get($id);
		return (FALSE!==$data) ? $data : '';
	}
	
	/*
	 * 写入
	 * @access public
	 * @param $id string 键值
	 * @param $data string 数据
	 * @return boolean
	 */
	public function _write($id, $data)
	{
		$access = time();
		$method = '_write_'.$this->save_mode;
		if (method_exists($this, $method))
		{
			return $this->$method($id, $data, $access);
		}
		return FALSE;
		
	}
	
	/*
	 * 写入数据库
	 * @access protected
	 * @param $id string 键值
	 * @param $data string 数据
	 * @return boolean
	 */
	protected function _write_database($id, $data, $access)
	{
		if ( ! $this->db)
		{
			return FALSE;
		}
		
		//使用mysql的一个过滤函数过滤下
		$id = mysql_real_escape_string($id);
		$data = mysql_real_escape_string($data);
		$sql = "REPLACE INTO ".$this->db_table." VALUES('".$id."', '".$data."', ".$access.")";
		return mysql_query($sql, $this->db);
	}
	
	/*
	 * 写入memcached
	 * @access protected
	 * @param $id string 键值
	 * @param $data string 数据
	 * @return boolean
	 */
	protected function _write_memcached($id, $data, $access)
	{
		if ( ! $this->memcached)
		{
			return FALSE;
		}
		return $this->memcached->set($id, $data, $this->lifetime);
	}
	
	/*
	 * 销毁
	 * @access public
	 * @param $id string 键值
	 * @return boolean
	 */
	public function _destroy($id)
	{
		$method = '_destroy_'.$this->save_mode;
		if (method_exists($this, $method))
		{
			return $this->$method($id);
		}
		return FALSE;
	}
	
	/*
	 * 从数据库销毁
	 * @access protected
	 * @param $id string 键值
	 * @return boolean
	 */
	protected function _destroy_database($id)
	{
		if ( ! $this->db)
		{
			return FALSE;
		}
		
		//使用mysql的一个过滤函数过滤下
		$id = mysql_real_escape_string($id);
		$sql = "DELETE FROM ".$this->db_table." WHERE `session_id` = '".$id."'";
		return mysql_query($sql, $this->db);
	}
	
	/*
	 * 销毁
	 * @access protected
	 * @param $id string 键值
	 * @return boolean
	 */
	protected function _destroy_memcached($id)
	{
		if ( ! $this->memcached)
		{
			return FALSE;
		}
		return $this->memcached->delete($id);
	}
	
	/*
	 * 垃圾回收
	 * @access public
	 * @param $max integer 最大时间
	 * @return boolean
	 */
	public function _gc($max)
	{
		$method = '_gc_'.$this->save_mode;
		if (method_exists($this, $method))
		{
			return $this->$method($max);
		}
		return FALSE;
	}
	
	/*
	 * 从数据库垃圾回收
	 * @access protected
	 * @param $max integer 最大时间
	 * @return boolean
	 */
	protected function _gc_database($max)
	{
		//使用mysql的一个过滤函数过滤下
		$max = mysql_real_escape_string($max);
		$old = time()-$max;
		$sql = "DELETE FROM ".$this->db_table." WHERE `session_last_access` < ".$old;
		return mysql_query($sql, $this->db);
	}
	
	/*
	 * memcached垃圾回收
	 * @access protected
	 * @param $max integer 最大时间
	 * @return boolean
	 */
	protected function _gc_memcached($max)
	{
		return TRUE;
	}

}